#pragma once
#include "stdafx.h"
#include "aps.h"
#include <fstream>

namespace aps {
static BYTE* readFile(const char* path, int *len)
{
        char * mem;
        int size;

        ifstream file (path, ios::in|ios::binary|ios::ate);
        if (file.is_open()) {
                size = file.tellg();
                mem = new char [size];
                file.seekg (0, ios::beg);
                file.read (mem, size);
                file.close();
                *len = size;
                return (BYTE*) mem;
        } else return NULL;
}

/*
Computes the check digit of the data and returns.
Arguments: BYTE* data: check digit is to be computed of the data
Returns: BYTE* of check digit
*/

static int CheckDigit(BYTE* data, int size) {
        int i = 0;
	int total = 0;
        int weight[256];
	for(i=0;i<10;i++) weight[i+'0'] = i;
        for(i=0;i<27;i++) weight[i+'A'] = 10+i;
        weight['<'] = 0;

        for(i=0;i<size;i=i+3)
                total += 7*weight[data[i]] + 3*weight[data[i+1]] + weight[data[i+2]];
	return (total % 10);
}

/*
Computes the composite digit and returns.
*/
static int getCompositeDigit(BYTE* data, int size) {
	int total = 0;
	int weight[256];
        int i = 0;
        for(i=0;i<10;i++) weight[i+'0'] = i;
        for(i=0;i<27;i++) weight[i+'A'] = 10+i;
        weight['<'] = 0;	
	
	for(i=44;i<53;i=i+3) 
		total += 7*weight[data[i]] + 3*weight[data[i+1]] + weight[data[i+2]];
	total += 7*weight[data[53]];
	for(i=57;i<63;i=i+3) 
		total += 3*weight[data[i]] + weight[data[i+1]] + 7*weight[data[i+2]];
	total += 3*weight[data[63]];
	for(i=65;i<86;i=i+3)
		total += weight[data[i]] + 7*weight[data[i+1]] + 3*weight[data[i+2]];
	total += weight[data[86]];
	return total % 10;
}

/*
Function returns the height and width brom the binary of jpeg image.
Arguments: BYTE* imgData: image data
	   BYTE* height: height should be stored in it
	   BYTE* width: width should be stored in it
Returns: void
*/
static void getHeightWidth(BYTE* imageData, BYTE* height, BYTE* width) {
	int i = 0;
	while(1) {
		if(imageData[i] == 0xFF) {
			if(imageData[i+1] >= 0xC0 && imageData[i+1] <= 0xC3) {
				memcpy(height, imageData+i+5, 2);
				memcpy(width, imageData+i+7, 2);
				return;
			}
			else i++;
		}
		else i++;
	}
}

/*
Function creates Data Group 1 by retrieving required entries from database correspoing to file number.
Data Group 1 contains information as presented on the Machine Readable Zone of Passport. It contains Document Type, Issuing State, Holder Name, Document Number, Nationality, Date of Birth, Sex, Date of Expiry, Optional data and certain check digits.
Arguments: const char* fileNo: file number corresponding to passport to make
	   BYTE* dgBytes: creates the data group and saves in dgBytes
	   int len: initial space allocated to dgBytes
Returns: On success, returns length of data saved in dgBytes
	 On insufficient space returns ERROR_MEM_INSUF
	 On failure in retreival from database, returns CREATEDG_FAILURE
*/
int createDG1(const char* fileNo, BYTE* dgBytes, int len) {
	PassportInfo ^pi = gcnew PassportInfo();
	DO* dg;
	dg = new DO(TAG_DG1);
	BYTE* array = (BYTE*)malloc(MRZ_SIZE);

	memcpy(array+0, dtype, 2);
	memcpy(array+2, country, 3);

	BYTE* truncName;
	truncName = (BYTE*)malloc(39);
	if(pi->getTruncatedName(fileNo, truncName) == SUCCESS) memcpy(array+5, truncName, 39);
	else return CREATEDG_FAILURE;

	BYTE* docNum;
	docNum = (BYTE*)malloc(10);
	if(pi->getDocNumber(fileNo, docNum) == SUCCESS) memcpy(array+44, docNum, 9);
	else return CREATEDG_FAILURE;

	int checkDocNum = CheckDigit(docNum, 9);
	array[53] = num[checkDocNum];

	BYTE* nation;
	nation = (BYTE*)malloc(3);
	if(pi->getNationality(fileNo, nation) == SUCCESS) memcpy(array+54, nation, 3);
	else return CREATEDG_FAILURE;

	BYTE* dob;
	dob = (BYTE*)malloc(10);
	if(pi->getDOB(fileNo, dob) == SUCCESS) memcpy(array+57, dob, 6);
	else return CREATEDG_FAILURE;

	int checkDOB = CheckDigit(dob, 6);
	array[63] = num[checkDOB];

	BYTE* sex;
	sex = (BYTE*)malloc(1);
	if(pi->getSex(fileNo, sex) == SUCCESS) memcpy(array+64, sex, 1);
	else return CREATEDG_FAILURE;

	BYTE* doe;
	doe = (BYTE*)malloc(10);
	if(pi->getDOE(fileNo, doe) == SUCCESS) memcpy(array+65, doe, 6);
	else return CREATEDG_FAILURE;

	int checkDOE = CheckDigit(doe, 6);
	array[71] = num[checkDOE];

	memcpy(array+72, optData, 15);
	int compDigit = getCompositeDigit(array, 39);
	array[87] = num[compDigit];

	DO *d = new DO(TAG_MRZ, MRZ_SIZE, array);
	dg->appendDO(*d);
	if(dg->getTotalLength() > len) return ERROR_MEM_INSUF;
	else {	
		dg->getTotalBytes(dgBytes);
		return dg->getTotalLength();
	}
}

/*
Function stores encoded face of the individual in DG2. Picture should be in jpeg2000 format.
Arguments: const char* fileNo: file number correspoing to passport to make
	   BYTE* dgBytes: creates data group and saves in dgBytes
	   int len: initial space allocated to dgBytes
Returns: On success, returns length of data saved in dgBytes
	 On insufficient space returns ERROR_MEM_INSUF
	 On failure in retreival from database, returns CREATEDG_FAILURE
*/
int createDG2(const char* fileNo, BYTE* dgBytes, int len) {
	PassportInfo ^pi = gcnew PassportInfo();
	DO* dg = new DO(TAG_DG2);
	list<DO> l;
	DO* btype = new DO(TAG_BIOM_TYPE, 1, bty);
	l.push_back(*btype);
	DO* bfeat = new DO(TAG_BIOM_FEAT, 1, bfe);
	l.push_back(*bfeat);
	DO* formatOwner = new DO(TAG_FORMAT_OWNER, 2, fowner);
	l.push_back(*formatOwner);
	DO* formatType = new DO(TAG_FORMAT_TYPE, 2, ftype);
	l.push_back(*formatType);
	Sequence *s = new Sequence(l);
	Tagged *t = new Tagged(0, TAG_BIOM_HEADER, s);
	
	BYTE* imageData = (BYTE*)malloc(len);
	int size = len;
	imageData = aps::readFile("FaceImage.jpg", &size);
	int imgBlockLen = size + 0x20;
	int recLen = imgBlockLen + 0x0E;
	BYTE* height;
	BYTE* width;
	height = (BYTE*)malloc(2);
	width = (BYTE*)malloc(2);
	getHeightWidth(imageData, height, width);
	BYTE hdr[46];
	memcpy(hdr+0, formatIdent, 4);
	memcpy(hdr+4, version, 4);
	int i;
	for(i=3;i>=0;--i)
		*((hdr+8)+(3-i)) = (recLen >> (8 * i) ) & 0xff;
	memcpy(hdr+12, numFaces, 2);
	for(i=3;i>=0;--i)
		*((hdr+14)+(3-i)) = (imgBlockLen >> (8 * i) ) & 0xff;
	memcpy(hdr+18, numFeat, 2);
	memcpy(hdr+20, feats, 14);
	memcpy(hdr+34, imageType, 1);
	memcpy(hdr+35, dataType, 1);
	memcpy(hdr+35, dataType, 1);
	memcpy(hdr+36, width, 2);
	memcpy(hdr+38, height, 2);
	memcpy(hdr+40, imgSpecs, 6);

	BYTE* data;
	data = (BYTE*)malloc(size + 46);
	memcpy(data, hdr, 46);
	memcpy(data+46, imageData, size);
	
	DO* bdb = new DO(TAG_BDB, size+46, data);
	list<DO> l1;
	l1.push_back(*t);
	l1.push_back(*bdb);
	Sequence *s1 = new Sequence(l1);
	Tagged *t1 = new Tagged(0, TAG_BIOM_INFO_TEMP, s1);
	list<DO> l2;

	BYTE n1[1];
	n1[0] = 1;
	ASNInteger *id;
	id = new ASNInteger(n1, 1);
	l2.push_back(*id);
	l2.push_back(*t1);
	Sequence *s2 = new Sequence(l2);
	Tagged *t2 = new Tagged(0, TAG_BIOM_INFO_GROUP, s2);
	dg->appendDO(*t2);
	if(dg->getTotalLength() > len) return ERROR_MEM_INSUF;
	else {	
		dg->getTotalBytes(dgBytes);
		return dg->getTotalLength();
	}
}

/*
Function creates Data Group 11 by retrieving required entries from database correspoing to file number.
Data Group 11 contains information regarding full name, information about aliases, place of birth, date of birth and permanent address.
Arguments: const char* fileNo: file number corresponding to passport to make
	   BYTE* dgBytes: creates the data group and saves in dgBytes
	   int len: initial space allocated to dgBytes
Returns: On success, returns length of data saved in dgBytes
	 On insufficient space returns ERROR_MEM_INSUF
	 On failure in retreival from database, returns CREATEDG_FAILURE
*/
int createDG11(const char* fileNo, BYTE* dgBytes, int len) {
	PassportInfo ^pi = gcnew PassportInfo();
	DO* d = new DO(TAG_LIST);
	DO* t;
	DO* dg;
	list<DO> l;
	list<DO> lt;
	int ret;
	
	BYTE* fullName;
	fullName = (BYTE*)malloc(100);
	ret = pi->getFullName(fileNo, fullName);
	d->appendValue(2, FULL_NAME);
	t = new DO(TAG_FULL_NAME, ret, fullName);
	l.push_back(*t);

	BYTE* others;
	others = (BYTE*)malloc(50);
	int sizebuf;
	ret = pi->getNumOtherNames(fileNo, others, &sizebuf);
	BYTE* name;
	name = (BYTE*)malloc(100);
	if(ret != FAILURE) {
		d->appendValue(1, CONTEXT_SPECIFIC_CONSTRUCTED);
		ASNInteger* id = new ASNInteger(others, sizebuf);
		lt.push_back(*id);
		int j = 0;
		int k = 0;
		for(j=0;j<ret;j++) {
			k = pi->getOtherName(fileNo, name, j);
			t = new DO(TAG_OTHER_NAME, k, name);
			lt.push_back(*t);
		}
	}
	ContextSpecific *asn;
	asn = new ContextSpecific(0, lt);		
	l.push_back(*asn);
	
	BYTE* placeBirth;
	placeBirth = (BYTE*)malloc(50);
	ret = pi->getPlaceBirth(fileNo, placeBirth);
	d->appendValue(2, PLACE_BIRTH);
	t = new DO(TAG_PLACE_BIRTH, ret, placeBirth);
	l.push_back(*t);
	
	BYTE* fullDOB;
	fullDOB = (BYTE*)malloc(10);
	ret = pi->getFullDOB(fileNo, fullDOB);
	d->appendValue(2, FULL_DOB);
	t = new DO(TAG_FULL_DOB, ret, fullDOB);
	l.push_back(*t);

	BYTE* permAddr;
	permAddr = (BYTE*)malloc(500);
	ret = pi->getPermAddr(fileNo, permAddr);
	d->appendValue(2, PERM_ADDRESS);
	t = new DO(TAG_PERM_ADDRESS, ret, permAddr);
	l.push_back(*t);

	l.push_front(*d);
	Sequence *s = new Sequence(l);
	dg = new Tagged(0, TAG_DG11, s);
	if(dg->getTotalLength() > len) return ERROR_MEM_INSUF;
	else {	
		dg->getTotalBytes(dgBytes);
		return dg->getTotalLength();
	}
}

/*
Function creates Data Group 12 by retrieving required entries from database.
Data Group 12 contains information regarding issuing authority, date of issue and timestamp of personalization.
Arguments: const char* fileNo: file number corresponding to passport to make
	   BYTE* dgBytes: creates the data group and saves in dgBytes
	   int len: initial space allocated to dgBytes
Returns: On success, returns length of data saved in dgBytes
	 On insufficient space returns ERROR_MEM_INSUF
	 On failure in retreival from database, returns CREATEDG_FAILURE
*/
int createDG12(const char* fileNo, const char* address, BYTE* dgBytes, int len) {
	PassportInfo ^pi = gcnew PassportInfo();
	DO* d = new DO(TAG_LIST);
	DO* t;
	DO* dg; 
	list<DO> l;

	int ret;
	BYTE* issueAuth = (BYTE*)malloc(100);
	ret = pi->getIssueAuth(address, issueAuth);
	d->appendValue(2, ISSUE_AUTH);
	t = new DO(TAG_ISSUE_AUTH, ret, issueAuth);
	l.push_back(*t);
	
	//at the time of signing dateIssue and dtTimePers would be updated	
	BYTE* dateIssue;
	dateIssue = (BYTE*)malloc(50);
	ret = pi->getDateIssue(fileNo, dateIssue);
	d->appendValue(2, DATE_ISSUE);
	t = new DO(TAG_DATE_ISSUE, ret, dateIssue);
	l.push_back(*t);
	
	BYTE* dtTimePers;
	dtTimePers = (BYTE*)malloc(20);
	ret = pi->getTimestampPers(fileNo, dtTimePers);
	d->appendValue(2, TIMESTAMP_PERSONAL);
	t = new DO(TAG_TIMESTAMP_PERSONAL, ret, dtTimePers);
	l.push_back(*t);
	
	l.push_front(*d);
	
	Sequence *s = new Sequence(l);
	dg = new Tagged(0, TAG_DG12, s);
	if(dg->getTotalLength() > len) return ERROR_MEM_INSUF;
	else {	
		dg->getTotalBytes(dgBytes);
		return dg->getTotalLength();
	}
}

/*
Function creates Data Group 13 by retrieving required entries from database.
Data Group 13 contains information about place of issue, fathers name, mothers name, spouse name, all old passport details and file number correspoding to passport.
Arguments: const char* fileNo: file number corresponding to passport to make
	   BYTE* dgBytes: creates the data group and saves in dgBytes
	   int len: initial space allocated to dgBytes
Returns: On success, returns length of data saved in dgBytes
	 On insufficient space returns ERROR_MEM_INSUF
	 On failure in retreival from database, returns CREATEDG_FAILURE
*/
int createDG13(const char* fileNo, BYTE* dgBytes, int len) {
	PassportInfo ^pi = gcnew PassportInfo();
	DO* dg;
	DO* t;
	DO* d = new DO(TAG_LIST);
	list<DO> l;

	int ret;	
	BYTE* pissue;
	pissue = (BYTE*)malloc(50);
	ret = pi->getPlaceIssue(fileNo, pissue);
	d->appendValue(1, PLACE_ISSUE);
	t = new DO(TAG_PLACE_ISSUE, ret, pissue);
	l.push_back(*t);

	BYTE* fname;
	fname = (BYTE*)malloc(100);
	ret = pi->getFathersName(fileNo, fname);
	if(ret != FAILURE) {
		d->appendValue(1, FATHERS_NAME);
		t = new DO(TAG_FATHERS_NAME, ret, fname);
		l.push_back(*t);
	}
	
	BYTE* mname;
	mname = (BYTE*)malloc(100);
	ret = pi->getMothersName(fileNo, mname);
	if(ret != FAILURE) {
		d->appendValue(1, MOTHERS_NAME);
		t = new DO(TAG_MOTHERS_NAME, ret, mname);
		l.push_back(*t);
	}
	
	BYTE* sname;
	sname = (BYTE*)malloc(100);
	ret = pi->getSpouseName(fileNo, sname);
	if(ret != FAILURE) {
		d->appendValue(1, SPOUSE_NAME);
		t = new DO(TAG_SPOUSE_NAME, ret, sname);
		l.push_back(*t);
	}
	
	char* pNum = (char*)malloc(20);
	pNum = (char*)fileNo;
	char* nNum = (char*)malloc(20);
	int i = 0;
	BYTE* docNum = (BYTE*)malloc(9);
	BYTE* place = (BYTE*)malloc(50);
	BYTE* date = (BYTE*)malloc(10);
	list<DO> lt;

	int k = pi->getOldPassportNum(pNum, nNum);
	//got nNum should be equal to 111222333
	if(k != FAILURE) d->appendValue(1, OLD_PASSPORT);
	while(k != FAILURE) {
		i++;
		ret = pi->getDocNumber((const char*)nNum, docNum);
		t = new DO(TAG_OLD_PASSPORT_NUM, 9, docNum);
		lt.push_back(*t);
		
		ret = pi->getPlaceIssue((const char*)nNum, place);
		t = new DO(TAG_OLD_PASSPORT_ISSUE, ret, place);
		lt.push_back(*t);
		
		ret = pi->getDateIssue((const char*)nNum, date);
		t = new DO(TAG_OLD_PASSPORT_DATE, ret, date);
		lt.push_back(*t);
		
		pNum = nNum;
		k = pi->getOldPassportNum(pNum, nNum);
	}
	
	BYTE n1;
	n1 = i;
	ASNInteger *id;
	id = new ASNInteger(&n1, 1);	
	lt.push_front(*id);

	ContextSpecific *asn;
	asn = new ContextSpecific(0, lt);
	l.push_back(*asn);
	t = new DO(TAG_FILE_NUMBER, strlen(fileNo), (BYTE*)fileNo);
	d->appendValue(1, FILE_NUMBER);
	l.push_back(*t);

	l.push_front(*d);
	
	Sequence *s = new Sequence(l);
	dg = new Tagged(0, TAG_DG13, s);
	if(dg->getTotalLength() > len) return ERROR_MEM_INSUF;
	else {	
		dg->getTotalBytes(dgBytes);
		return dg->getTotalLength();
	}
}
}